/**
 * Copyright (c) 2004-2006 Regents of the University of California.
 * See "license-prefuse.txt" for licensing terms.
 */
package core.proto.vis;

import java.awt.geom.Rectangle2D;

import javax.swing.JFrame;

import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.assignment.SizeAction;
import prefuse.action.filter.GraphDistanceFilter;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.Control;
import prefuse.controls.FocusControl;
import prefuse.controls.NeighborHighlightControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Graph;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.io.DataIOException;
import prefuse.data.io.GraphMLReader;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.render.Renderer;
import prefuse.render.ShapeRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphicsLib;
import prefuse.util.display.DisplayLib;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;

public class VizDemo extends Display
{

    public static final String GRAPH = "graph";

    public static final String NODES = "graph.nodes";

    public static final String EDGES = "graph.edges";

    public VizDemo( int width, int height, String fileName, int edge ) {
        // initialize display and data
        super( new Visualization() );
        initDataGroups( fileName );

        // set up the renderers
        // draw the nodes as basic shapes
        Renderer nodeR = new ShapeRenderer( 20 );

        // use label renderer
        LabelRenderer labelR = new LabelRenderer( "name" );
        labelR.setRoundedCorner( 80, 80 ); // round the corners

        DefaultRendererFactory drf = new DefaultRendererFactory();
        // drf.setDefaultRenderer( nodeR );
        drf.setDefaultRenderer( labelR );
        m_vis.setRendererFactory( drf );

        setGraph( (Graph) m_vis.getSourceData( GRAPH ), "name" );

        // set on-focus listener
        TupleSet focusGroup = m_vis.getGroup( Visualization.FOCUS_ITEMS );
        focusGroup.addTupleSetListener( new TupleSetListener()
        {
            public void tupleSetChanged( TupleSet ts, Tuple[] add, Tuple[] rem ) {
                for( int i = 0; i < rem.length; ++i )
                    ((VisualItem) rem[i]).setFixed( false );
                for( int i = 0; i < add.length; ++i ) {
                    ((VisualItem) add[i]).setFixed( false );
                    ((VisualItem) add[i]).setFixed( true );
                }
                if( ts.getTupleCount() == 0 ) {
                    ts.addTuple( rem[0] );
                    ((VisualItem) rem[0]).setFixed( false );
                }
                m_vis.run( "draw" );
            }
        } );

        // set up the visual operators
        // first set up all the color actions
        int[] palette = new int[] { ColorLib.rgb( 255, 180, 180 ),
                ColorLib.rgb( 190, 190, 255 ) };
        // map nominal data values to colors using our provided palette
        DataColorAction fill = new DataColorAction( NODES, "type",
                Constants.NOMINAL, VisualItem.FILLCOLOR, palette );
        // use black for node text
        ColorAction text = new ColorAction( NODES, VisualItem.TEXTCOLOR,
                ColorLib.gray( 0 ) );
        fill.add( VisualItem.FIXED, ColorLib.rgb( 255, 100, 100 ) );
        fill.add( VisualItem.HIGHLIGHT, ColorLib.rgb( 255, 200, 125 ) );

        // use light grey for edges
        ColorAction edges = new ColorAction( EDGES, VisualItem.STROKECOLOR,
                ColorLib.gray( 0 ) );
        SizeAction edgeSize = new SizeAction( EDGES, edge );

        // draw
        ActionList draw = new ActionList();
        draw.add( fill );
        draw.add( text );
        draw.add( edges );
        draw.add( edgeSize );
        m_vis.putAction( "draw", draw );

        // layout
        ActionList layout = new ActionList( Activity.INFINITY );
        layout.add( new ForceDirectedLayout( GRAPH ) );
        layout.add( new RepaintAction() );
        m_vis.putAction( "layout", layout );

        // repaint
        ActionList repaint = new ActionList();
        repaint.add( new RepaintAction() );
        m_vis.putAction( "repaint", repaint );

        setToolTipText( "Scroll to zoom; left-click to pan" );

        setHighQuality( true );
        addControlListener( new WheelZoomControl() );
        addControlListener( new ZoomToFitControl( Control.MIDDLE_MOUSE_BUTTON ) );
        addControlListener( new PanControl() );
        addControlListener( new NeighborHighlightControl() );
        addControlListener( new FocusControl( 0 ) );

        // set node distance
        int hops = 30;
        final GraphDistanceFilter filter = new GraphDistanceFilter( GRAPH, hops );
        filter.setDistance( 500 );

        // set things running
        resizeAndRun( width, height );
    }

    public void resizeAndRun( int width, int height ) {
        setSize( width, height );
        zoomToFit();
        m_vis.run( "draw" );
        m_vis.run( "layout" );
        m_vis.run( "repaint" );
    }

    private void zoomToFit() {
        long duration = 2000;
        int margin = 100;

        Visualization vis = getVisualization();
        Rectangle2D bounds = vis.getBounds( GRAPH );
        GraphicsLib.expand( bounds, margin + (int) (1 / getScale()) );
        DisplayLib.fitViewToBounds( this, bounds, duration );
    }

    /**
     * Load the data
     */
    private void initDataGroups( String fileName ) {
        // create sample graph
        Graph graph = null;
        try {
            graph = new GraphMLReader().readGraph( fileName );
        } catch( DataIOException e ) {
            e.printStackTrace();
            System.err.println( "Error loading graph. Exiting..." );
            System.exit( 1 );
        }

        // add visual data groups
        m_vis.add( GRAPH, graph );
        m_vis.setInteractive( EDGES, null, false );
        // m_vis.setValue( NODES, null, VisualItem.SHAPE, new Integer(
        // Constants.SHAPE_ELLIPSE ) );
    }

    public static void main( String[] argv ) {
        JFrame frame = demo( 500, 300, "data/config/vizdemo3.xml", 2 );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setVisible( true );
    }

    public static JFrame demo( int width, int height, String fileName,
            int edgeSize ) {
        VizDemo ad = new VizDemo( width, height, fileName, edgeSize );
        JFrame frame = new JFrame( "Case Relations" );
        frame.getContentPane().add( ad );
        frame.pack();
        return frame;
    }

    public void setGraph( Graph g, String label ) {
        // update labelling
        DefaultRendererFactory drf = (DefaultRendererFactory) m_vis
                .getRendererFactory();
        ((LabelRenderer) drf.getDefaultRenderer()).setTextField( label );

        // update graph
        m_vis.removeGroup( GRAPH );
        VisualGraph vg = m_vis.addGraph( GRAPH, g );
        m_vis.setValue( EDGES, null, VisualItem.INTERACTIVE, Boolean.FALSE );
        VisualItem f = (VisualItem) vg.getNode( 0 );
        m_vis.getGroup( Visualization.FOCUS_ITEMS ).setTuple( f );
        f.setFixed( false );
    }
}